using System;
using System.Collections.Generic;

namespace AndroidUnevenRoadGame.Game
{
    /// <summary>
    /// 玩家基础状态与牌组、能量管理。
    /// </summary>
    public sealed class PlayerState
    {
        public CharacterDefinition Character { get; private set; } = CharacterDefinition.Default;
        public int MaxHp { get; private set; }
        public int CurrentHp { get; private set; }
        public int Block { get; private set; }
        public int EnergyPerTurn { get; private set; } = 3;
        public int CurrentEnergy { get; private set; }

        public List<string> DrawPile { get; } = new();
        public List<string> Hand { get; } = new();
        public List<string> DiscardPile { get; } = new();
        public List<string> ExhaustPile { get; } = new();

        public void Initialize(CharacterDefinition definition)
        {
            Character = definition;
            MaxHp = definition.InitialHp;
            CurrentHp = MaxHp;
            Block = 0;
            EnergyPerTurn = definition.EnergyPerTurn;
            CurrentEnergy = EnergyPerTurn;

            ResetDeck(definition.StartingDeck);
        }

        public void ResetDeck(IReadOnlyList<string> startingDeck)
        {
            DrawPile.Clear();
            foreach (var cardId in startingDeck)
            {
                DrawPile.Add(cardId);
            }
            Hand.Clear();
            DiscardPile.Clear();
            ExhaustPile.Clear();
            Shuffle(DrawPile);
        }

        public void StartTurn()
        {
            Block = 0;
            CurrentEnergy = EnergyPerTurn;
        }

        public void SpendEnergy(int amount)
        {
            if (amount > CurrentEnergy)
            {
                throw new InvalidOperationException("能量不足");
            }

            CurrentEnergy -= amount;
        }

        public void GainEnergy(int amount)
        {
            CurrentEnergy = Math.Max(0, CurrentEnergy + amount);
        }

        public void GainBlock(int amount)
        {
            Block = Math.Max(0, Block + amount);
        }

        public void TakeDamage(int amount)
        {
            var damageAfterBlock = Math.Max(0, amount - Block);
            Block = Math.Max(0, Block - amount);
            CurrentHp = Math.Max(0, CurrentHp - damageAfterBlock);
        }

        public void Heal(int amount)
        {
            CurrentHp = Math.Min(MaxHp, CurrentHp + amount);
        }

        public void AddCardToDeck(string cardId, bool shuffle = true)
        {
            DrawPile.Add(cardId);
            if (shuffle)
            {
                Shuffle(DrawPile);
            }
        }

        public IEnumerable<string> EnumerateAllCards()
        {
            foreach (var card in Hand)
            {
                yield return card;
            }
            foreach (var card in DrawPile)
            {
                yield return card;
            }
            foreach (var card in DiscardPile)
            {
                yield return card;
            }
            foreach (var card in ExhaustPile)
            {
                yield return card;
            }
        }

        public bool RemoveCard(string cardId)
        {
            if (RemoveFrom(Hand, cardId)) return true;
            if (RemoveFrom(DrawPile, cardId)) return true;
            if (RemoveFrom(DiscardPile, cardId)) return true;
            if (RemoveFrom(ExhaustPile, cardId)) return true;
            return false;
        }

        public bool UpgradeCard(string cardId)
        {
            if (!CardDatabase.TryGetUpgrade(cardId, out var upgraded))
            {
                return false;
            }

            return ReplaceCard(cardId, upgraded.Id);
        }

        public int UpgradeCards(IEnumerable<string> cardIds)
        {
            var upgradedCount = 0;
            foreach (var cardId in cardIds)
            {
                if (UpgradeCard(cardId))
                {
                    upgradedCount++;
                }
            }
            return upgradedCount;
        }

        private static bool RemoveFrom(List<string> list, string cardId)
        {
            var index = list.IndexOf(cardId);
            if (index >= 0)
            {
                list.RemoveAt(index);
                return true;
            }
            return false;
        }

        private bool ReplaceCard(string oldId, string newId)
        {
            if (ReplaceInList(Hand, oldId, newId)) return true;
            if (ReplaceInList(DrawPile, oldId, newId)) return true;
            if (ReplaceInList(DiscardPile, oldId, newId)) return true;
            if (ReplaceInList(ExhaustPile, oldId, newId)) return true;
            return false;
        }

        private static bool ReplaceInList(List<string> list, string oldId, string newId)
        {
            var index = list.IndexOf(oldId);
            if (index >= 0)
            {
                list[index] = newId;
                return true;
            }
            return false;
        }

        public void DrawCards(int amount)
        {
            for (int i = 0; i < amount; i++)
            {
                if (DrawPile.Count == 0)
                {
                    if (DiscardPile.Count == 0)
                    {
                        break;
                    }
                    ReshuffleDiscardIntoDraw();
                }

                var card = DrawPile[^1];
                DrawPile.RemoveAt(DrawPile.Count - 1);
                Hand.Add(card);
            }
        }

        public void DiscardHand()
        {
            DiscardPile.AddRange(Hand);
            Hand.Clear();
        }

        public void ExhaustCard(string cardId)
        {
            if (Hand.Remove(cardId))
            {
                ExhaustPile.Add(cardId);
            }
        }

        public void DiscardRandom(int count)
        {
            var rng = GameRoot.Instance.Random;
            for (int i = 0; i < count && Hand.Count > 0; i++)
            {
                var index = rng.RandiRange(0, Hand.Count - 1);
                var cardId = Hand[index];
                Hand.RemoveAt(index);
                DiscardPile.Add(cardId);
            }
        }

        private void ReshuffleDiscardIntoDraw()
        {
            DrawPile.AddRange(DiscardPile);
            DiscardPile.Clear();
            Shuffle(DrawPile);
        }

        private static void Shuffle(List<string> list)
        {
            var rng = GameRoot.Instance.Random;
            for (int i = list.Count - 1; i > 0; i--)
            {
                var j = rng.RandiRange(0, i);
                (list[i], list[j]) = (list[j], list[i]);
            }
        }
    }
}

